import { select, selectAll } from 'd3'
import { hasChild, getTargetDataById, copyOrCutXmindNode } from '../utils'
import { hideElementControlNode, edgeRoughOptions } from '../graph'
import { getEdgeStartEndCoordinate } from '../utils/math'
import lineShapeMap from '../line-shape'

let dragIngSubject = null
let dragTargetAttrs = null

/**
 * 鼠标移入主题
 * @param {*} _event
 * @param {*} _this
 * @returns
 */
export function nodeHandlerMouseEnter (_event, _this) {
  if (dragIngSubject) {
    const data = select(_this).datum()
    if (data.data.isRoot || (data.data.pos && hasChild(data.data))) return
    const direction = data.direction
    const boundingClientRect = select(_this).node().getBoundingClientRect()
    const enterNodeInfo = {
      x: data.x,
      y: data.y,
      width: data.width,
      height: data.height,
      top: boundingClientRect.top,
      left: boundingClientRect.left,
      hasChild: !!data.children?.length,
      id: data.data._id,
      pos: data.data.pos
    }
    dragTargetAttrs = {
      direction,
      enterNodeInfo,
      parentNodeInfo: data.data.pos ? data : data.parent
    }
  }
}

/**
 * 鼠标移除主题
 */
export function nodeHandlerMouseLeave () {
  if (dragIngSubject) {
    dragTargetAttrs = null
    select('.drag-shadow-node').style('display', 'none')
    select('.drag-shadow-edge').style('display', 'none')
  }
}

/**
 * 主题拖拽中
 * @param {*} rc
 * @param {*} displaySheet
 * @param {*} event
 * @param {*} _this
 * @returns
 */
export function nodeHandlerDragIng (rc, displaySheet, event, _this) {
  event.sourceEvent.stopPropagation()
  const isRough = Boolean(Number(sessionStorage.getItem('isRough')))
  const subject = event.subject
  if (subject.data.isRoot) return
  if (!dragIngSubject) {
    const svg = select('#zx-xmind-map-svg')
    const childNodedata = select(_this)
      .classed('draging-elemet-graph-5', true)
      .raise().datum().descendants()
    childNodedata.forEach(node => {
      if (node.data._id !== subject.data._id) {
        select(`#${node.data._id}`).classed('draging-elemet-graph-2', true)
      }
      selectAll(`[data-id*=${node.data._id}]`).classed('draging-elemet-graph-2', true)
    })
    select('.mind-map-edgebox')
      .selectAll('g')
      .filter(path => childNodedata.find(o => o.data._id === path.target.data._id))
      .classed('draging-elemet-graph-2', true)
    select(_this)
      .clone(true)
      .classed('clone-draging-node draging-elemet-graph-2', true)
    dragIngSubject = subject
    svg.classed('moving', true)
    hideElementControlNode()
  }
  const { tx = 0, ty = 0 } = select(_this).datum()
  select(_this).datum().tx = tx + event.dx
  select(_this).datum().ty = ty + event.dy
  select(_this).attr('transform', `translate(${tx + event.dx}, ${ty + event.dy})`)
  if (dragTargetAttrs) {
    renderDragShadowNode(rc, displaySheet, event.sourceEvent, isRough)
  }
}

/**
 * 主题拖拽结束
 * @param {*} root
 * @param {*} event
 * @param {*} _this
 * @returns
 */
export function nodeHandlerDragEnd (root, event, _this) {
  if (!dragIngSubject) return false
  // 非独立主题拖动节点移动的距离小于15时不更新节点的拖动数据
  const distance = Math.pow(event.x - dragIngSubject.x, 2) + Math.pow(event.y - dragIngSubject.y, 2)
  if (distance < 15 * 15 && !dragIngSubject.data.pos) {
    select(_this).datum().tx = 0
    select(_this).datum().ty = 0
    select(_this).attr('transform', null)
    hideDragingElement()
    return false
  }
  if (dragTargetAttrs) {
    const { dir, atLast, enterNodeInfo, parentNodeInfo } = dragTargetAttrs
    const parentId = atLast ? enterNodeInfo.id : parentNodeInfo.data._id
    const parentNode = getTargetDataById(root, parentId)
    const dragTarget = copyOrCutXmindNode(root.children, root, dragIngSubject.data._id, true)
    dragTarget.pos = null
    if (!atLast) {
      const idx = parentNode.children.findIndex(o => o._id === enterNodeInfo.id)
      parentNode.children.splice(dir === 'up' ? idx : idx + 1, 0, dragTarget)
    } else {
      parentNode.children = [...(parentNode.children || []), dragTarget]
    }
    dragTargetAttrs = null
  } else {
    const dragTarget = copyOrCutXmindNode(root.children, root, dragIngSubject.data._id, true)
    dragTarget.pos = { x: event.x, y: event.y }
    root.children = [...root.children, dragTarget]
  }
  hideDragingElement()
  return true
}

/**
 * 移除拖拽中的主题占位图形及样式
 */
function hideDragingElement () {
  const svg = select('#zx-xmind-map-svg')
  select('.drag-shadow-node').style('display', 'none')
  select('.drag-shadow-edge').style('display', 'none')
  selectAll('.draging-elemet-graph-2').classed('draging-elemet-graph-2', false)
  selectAll('.draging-elemet-graph-5').classed('draging-elemet-graph-5', false)
  selectAll('.clone-draging-node').remove()
  svg.classed('moving', false)
  dragIngSubject = null
}

/**
 * 渲染拖动节点后节点即将存在位置的占位符
 * @param {*} rc
 * @param {*} displaySheet
 * @param {*} _event
 * @param {*} isRough
 */
export function renderDragShadowNode (rc, displaySheet, _event, isRough) {
  const dragShadowNode = select('.drag-shadow-node').style('display', 'block')
  const dragShadowEdge = select('.drag-shadow-edge').style('display', 'block')
  const { parentNodeInfo, enterNodeInfo, direction } = dragTargetAttrs
  const { width, height, top, left, hasChild, pos } = enterNodeInfo
  // 被拖拽的节点在目标节点的前面还是后面
  let [dir, atLast] = [null, false]
  if (direction !== 'bottom') {
    dir = top + (height / 2) * displaySheet.eventTransform.k > _event.y ? 'up' : 'down'
  } else {
    dir = left + (width / 2) * displaySheet.eventTransform.k > _event.x ? 'up' : 'down'
  }
  // 被拖拽的节点是否作为目标节点的子集
  if (direction === 'right') {
    atLast = ((width * displaySheet.eventTransform.k + left - _event.x < 20) && !hasChild) || Boolean(pos)
  } else if (direction === 'left') {
    atLast = ((_event.x - left) < 20 && !hasChild) || Boolean(pos)
  } else if (direction === 'bottom') {
    atLast = (height * displaySheet.eventTransform.k + top - _event.y < 12) && !hasChild
  }
  dragTargetAttrs.dir = dir
  dragTargetAttrs.atLast = atLast
  const { start, end, rect } = getEdgeStartEndCoordinate({ direction, parentNodeInfo, enterNodeInfo, dir, atLast })
  const isRoot = parentNodeInfo.data.isRoot
  const { lineWidth, branch } = parentNodeInfo.style.lineStyle
  const gradient = isRoot && typeof lineWidth === 'string'
  dragShadowNode.attr('transform', `translate(${rect.x}, ${rect.y})`)
  const path = lineShapeMap[branch]({
    sourcePoint: { sx: start.x, sy: start.y },
    targetPoint: { tx: end.x, ty: end.y },
    isRoot: !parentNodeInfo.parent && !atLast,
    gradient,
    lineWidth,
    direction
  })
  if (!isRough) {
    dragShadowEdge.select('path')
      .attr('d', path)
      .attr('fill', gradient ? 'rgb(4, 183, 250)' : 'transparent')
      .attr('stroke', 'rgb(4, 183, 250)')
      .attr('stroke-width', gradient ? 0.2 : lineWidth)
  } else {
    const node = rc.path(path, {
      fill: gradient || branch === '6' ? 'rgb(4, 183, 250)' : 'transparent',
      strokeWidth: gradient || branch === '6' ? 0.2 : lineWidth,
      stroke: 'rgb(4, 183, 250)',
      ...edgeRoughOptions
    })
    dragShadowEdge.html(select(node).html())
  }
}
